(function ($)
{
    $.fn.extend({
        loadmore: function (option)
        {
            var onMove = option.onMove || function () { }
            var onRefresh = option.onRefresh || function () { }
            var onLoadMore = option.onLoadMore || function () { }
            var parent = $(this)
            var child = parent.children()
            var pointDownY = 0
            var pointDownScrollTop = 0
            var maxScrollTop = 0
            var isTouchOrMouseDownStartFromChild = false
            child.bind('mousedown touchstart', function (e)
            {
                if (e.type == 'touchstart')
                {
                    pointDownY = e.originalEvent.changedTouches[0].clientY
                }
                else
                {
                    pointDownY = e.clientY
                }
                maxScrollTop = GetHeightWithMargin(child) - parent.height()
                maxScrollTop = maxScrollTop < 0 ? 0 : maxScrollTop
                pointDownScrollTop = parent.scrollTop()
                isTouchOrMouseDownStartFromChild = true
            })
            $(window).bind('mousemove touchmove', function (e)
            {
                e.preventDefault()
                if (!isTouchOrMouseDownStartFromChild)
                {
                    return
                }
                var y
                if (e.type == 'touchmove')
                {
                    y = e.originalEvent.changedTouches[0].clientY
                }
                else
                {
                    if (e.buttons != 1)
                    {
                        return
                    }
                    else
                    {
                        y = e.clientY
                    }
                }
                var dy = pointDownY - y
                var toScrollTop = 0
                var toTransformY = 0
                if (dy > 0)
                {
                    var tempScrollTop = maxScrollTop - pointDownScrollTop
                    if (dy <= tempScrollTop)
                    {
                        toScrollTop = pointDownScrollTop + dy
                    }
                    else
                    {
                        toScrollTop = maxScrollTop
                        toTransformY = -1 * (dy - tempScrollTop)
                    }
                }
                else
                {
                    if (-dy > pointDownScrollTop)
                    {
                        toScrollTop = 0
                        toTransformY = -dy - pointDownScrollTop
                    }
                    else
                    {
                        toScrollTop = pointDownScrollTop + dy
                    }
                }
                parent.scrollTop(toScrollTop)
                var toTransform = 'translateY(' + toTransformY + 'px)'
                child.css({
                    transform: toTransform
                })
                onMove(toTransformY)
            })

            $(window).bind('mouseup touchend', function (e)
            {
                var y
                isTouchOrMouseDownStartFromChild = false
                if (e.type == 'touchend')
                {
                    y = e.originalEvent.changedTouches[0].clientY
                }
                else
                {
                    y = e.clientY
                }
                var currentTransformTranslateY = GetTransformTranslateY(child)
                if (currentTransformTranslateY > 50)
                {
                    onRefresh(function ()
                    {
                        GoBack(0, pointDownY > y)
                    })
                }
                else if (currentTransformTranslateY < -50)
                {
                    onLoadMore(function ()
                    {
                        GoBack(0, pointDownY > y)
                    })
                }
                else
                {
                    GoBack(0, pointDownY > y)
                }
            })

            function GetTransformTranslateY(el)
            {
                var transform = el.css('transform')
                if (transform == 'none')
                {
                    transform = '(0, 0, 0)'
                }
                return transform.split(',').reverse()[0].split(')')[0].trim() * 1
            }

            function GoBack(toTransformTranslateY, isLoadMore)
            {
                var fromTransformTranslateY = GetTransformTranslateY(child)
                maxScrollTop = GetHeightWithMargin(child) - parent.height()
                maxScrollTop = maxScrollTop < 0 ? 0 : maxScrollTop
                if (fromTransformTranslateY != 0)
                {
                    parent.scrollTop(isLoadMore ? maxScrollTop : 0)
                }
                var duration = 100
                var interval = 5
                var steps = duration / interval
                var dsPerInterval = (toTransformTranslateY - fromTransformTranslateY) / steps
                var si = setInterval(function ()
                {
                    var currentTransformTranslateY = GetTransformTranslateY(child)
                    if (currentTransformTranslateY == toTransformTranslateY)
                    {
                        clearInterval(si)
                    }
                    else
                    {
                        var toTransform = ''
                        if (Math.abs(Math.abs(currentTransformTranslateY) - Math.abs(toTransformTranslateY)) <= Math.abs(dsPerInterval))
                        {
                            toTransform = 'translateY(' + toTransformTranslateY + 'px)'
                        }
                        else
                        {
                            toTransform = 'translateY(' + (currentTransformTranslateY + 1 * dsPerInterval) + 'px)'
                        }
                        child.css({
                            transform: toTransform
                        })
                    }
                }, interval)
            }

            function GetHeightWithMargin(el)
            {
                return el.outerHeight() + el.css('margin-top').replace('px', '') * 1 + el.css('margin-bottom').replace('px', '') * 1
            }

        }
    })
})(jQuery)